﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;

namespace jellybird.core
{
	public interface ITwMediaInfoFactory
	{
		bool Supported(TwEntityUrl entity_url);
		
		bool FetchProperies
			(TwEntityUrl entity_url,
			 out string OriginalUrl,
			 out string Title,
			 out string Description,
			 out int OriginalWidth,
			 out int OriginalHeight,
			 out IEnumerable<TwMediaThumbnail> Thumbnails);
	}

	#region YFROG
	public class TwMediaInfoFactoryYfrog:ITwMediaInfoFactory
	{
		bool ITwMediaInfoFactory.Supported(TwEntityUrl entity_url)
		{
			return entity_url.ExpandedUrl.StartsWith
				(TwMediaHelper.URL_YFROG,
				 StringComparison.OrdinalIgnoreCase);
		}
		
		bool ITwMediaInfoFactory.FetchProperies
			(TwEntityUrl entity_url,
			 out string OriginalUrl,
			 out string Title,
			 out string Description,
			 out int OriginalWidth,
			 out int OriginalHeight,
			 out IEnumerable<TwMediaThumbnail> Thumbnails)
		{
			OriginalUrl=null;
			Title=null;
			Description=null;
			OriginalWidth=0;
			OriginalHeight=0;
			List<TwMediaThumbnail> thumbnail_list=new List<TwMediaThumbnail>();
			Thumbnails=thumbnail_list;
			
			string expanded_url=entity_url.ExpandedUrl;
			Uri expanded_uri=new Uri(expanded_url,UriKind.Absolute);
			string id = TwMediaHelper.GetMediaIdFromUrlYfrog(expanded_uri);
			
			if (string.IsNullOrWhiteSpace(id))
				return false;
			
			XDocument yf_info=null;
			try
			{
				yf_info = TwMediaHelper.GetMediaInfoYfrog(id);
			}
			catch(System.Net.WebException web_ex)
			{
				#if DEBUG
				Helper.OnDebugMessage(string.Format("Error: '{0}' while get info for '{1}'",web_ex.Message,expanded_url));
				#endif
			}
			
			if(yf_info==null)
				return false;
			
			XNamespace def_ns = yf_info.Root.GetDefaultNamespace();
			
			Description = "Yfrog";
			OriginalHeight = int.Parse
				((string)
				 (from el in yf_info.Descendants(def_ns + "height") select el).First());
			OriginalUrl = expanded_url;
			OriginalWidth = int.Parse
				((string)
				 (from el in yf_info.Descendants(def_ns + "width") select el).First());

			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 100,
			                   	Width = 100,
			                   	ThumbnailUrl = expanded_url + ":small"
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 480,
			                   	Width = 480,
			                   	ThumbnailUrl = expanded_url + ":iphone"
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 480,
			                   	Width = 640,
			                   	ThumbnailUrl = expanded_url + ":medium"
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = OriginalHeight,
			                   	Width = OriginalWidth,
			                   	ThumbnailUrl = expanded_url
			                   });
			return true;
		}
	}
	#endregion

	#region TWITPIC
	public class TwMediaInfoFactoryTwitpic:ITwMediaInfoFactory
	{
		bool ITwMediaInfoFactory.Supported(TwEntityUrl entity_url)
		{
			return entity_url.ExpandedUrl.StartsWith
				(TwMediaHelper.URL_TWITPIC,
				 StringComparison.OrdinalIgnoreCase);
		}
		
		bool ITwMediaInfoFactory.FetchProperies
			(TwEntityUrl entity_url,
			 out string OriginalUrl,
			 out string Title,
			 out string Description,
			 out int OriginalWidth,
			 out int OriginalHeight,
			 out IEnumerable<TwMediaThumbnail> Thumbnails)
		{
			OriginalUrl=null;
			Title=null;
			Description=null;
			OriginalWidth=0;
			OriginalHeight=0;
			List<TwMediaThumbnail> thumbnail_list=new List<TwMediaThumbnail>();
			Thumbnails=thumbnail_list;
			
			string expanded_url=entity_url.ExpandedUrl;
			Uri expanded_uri=new Uri(expanded_url,UriKind.Absolute);
			string id = TwMediaHelper.GetMediaIdFromUrlTwitpic(expanded_url);
			
			if (string.IsNullOrWhiteSpace(id))
				return false;
			
			JObject tp_info=null;
			try
			{
				tp_info = TwMediaHelper.GetMediaInfoTwitpic(id);
			}
			catch(System.Net.WebException web_ex)
			{
				#if DEBUG
				Helper.OnDebugMessage(string.Format("Error: '{0}' while get info for '{1}'",web_ex.Message,expanded_url));
				#endif
			}
			if(tp_info==null)
				return false;
			
			Description = string.Format
				("{0}; Source: {1}",
				 tp_info["message"],
				 tp_info["source"]);
			OriginalWidth = int.Parse((string)tp_info["width"]);
			OriginalUrl = expanded_url;
			OriginalHeight = int.Parse((string)tp_info["height"]);
			

			//добавляем thumbnail'ы
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 75,
			                   	Width = 75,
			                   	ThumbnailUrl = (new Uri(expanded_uri, @"show/mini/" + id)).AbsoluteUri
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 150,
			                   	Width = 150,
			                   	ThumbnailUrl = (new Uri(expanded_uri, @"show/thumb/" + id)).AbsoluteUri
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = (int)(OriginalHeight * (600.0 / OriginalWidth)),
			                   	Width = 600,
			                   	ThumbnailUrl = (new Uri(expanded_uri, @"show/large/" + id)).AbsoluteUri
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = OriginalHeight,
			                   	Width = OriginalWidth,
			                   	ThumbnailUrl = (new Uri(expanded_uri, @"show/full/" + id)).AbsoluteUri
			                   });
			return true;
		}
	}
	#endregion

	#region INSTAGRAM
	public class TwMediaInfoFactoryInstagram:ITwMediaInfoFactory
	{
		bool ITwMediaInfoFactory.Supported(TwEntityUrl entity_url)
		{
			return entity_url.ExpandedUrl.StartsWith
				(TwMediaHelper.URL_INSTAGRAM,
				 StringComparison.OrdinalIgnoreCase);
		}
		
		bool ITwMediaInfoFactory.FetchProperies
			(TwEntityUrl entity_url,
			 out string OriginalUrl,
			 out string Title,
			 out string Description,
			 out int OriginalWidth,
			 out int OriginalHeight,
			 out IEnumerable<TwMediaThumbnail> Thumbnails)
		{
			OriginalUrl=null;
			Title=null;
			Description=null;
			OriginalWidth=0;
			OriginalHeight=0;
			List<TwMediaThumbnail> thumbnail_list=new List<TwMediaThumbnail>();
			Thumbnails=thumbnail_list;
			
			string expanded_url=entity_url.ExpandedUrl;
			Uri expanded_uri=new Uri(expanded_url,UriKind.Absolute);
			
			JObject ig_info=null;
			try
			{
				ig_info = TwMediaHelper.GetMediaInfoInstagram(expanded_url);
			}
			catch(System.Net.WebException web_ex)
			{
				#if DEBUG
				Helper.OnDebugMessage(string.Format("Error: '{0}' while get info for '{1}'",web_ex.Message,expanded_url));
				#endif
			}
			
			if(ig_info==null)
				return false;
			
			Description = (string)ig_info["title"];
			OriginalHeight = (int)ig_info["height"];
			OriginalUrl = (string)ig_info["url"];
			OriginalWidth = (int)ig_info["width"];
			
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 150,
			                   	Width = 150,
			                   	ThumbnailUrl = expanded_url + @"/media/?size=t"
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 306,
			                   	Width = 306,
			                   	ThumbnailUrl = expanded_url + @"/media/?size=m"
			                   });
			thumbnail_list.Add(new TwMediaThumbnail()
			                   {
			                   	Height = 612,
			                   	Width = 612,
			                   	ThumbnailUrl = expanded_url + @"/media/?size=l"
			                   });
			return true;
		}
	}
	#endregion

	#region YOUTUBE
	public class TwMediaInfoFactoryYoutube:ITwMediaInfoFactory
	{
		bool ITwMediaInfoFactory.Supported(TwEntityUrl entity_url)
		{
			return (entity_url.ExpandedUrl.StartsWith(TwMediaHelper.URL_YOUTUBE_1,StringComparison.OrdinalIgnoreCase))||
				(entity_url.ExpandedUrl.StartsWith(TwMediaHelper.URL_YOUTUBE_2,StringComparison.OrdinalIgnoreCase));
		}
		
		bool ITwMediaInfoFactory.FetchProperies
			(TwEntityUrl entity_url,
			 out string OriginalUrl,
			 out string Title,
			 out string Description,
			 out int OriginalWidth,
			 out int OriginalHeight,
			 out IEnumerable<TwMediaThumbnail> Thumbnails)
		{
			OriginalUrl=null;
			Title=null;
			Description=null;
			OriginalWidth=0;
			OriginalHeight=0;
			List<TwMediaThumbnail> thumbnail_list=new List<TwMediaThumbnail>();
			Thumbnails=thumbnail_list;
			
			string expanded_url=entity_url.ExpandedUrl;
			Uri expanded_uri=new Uri(expanded_url,UriKind.Absolute);
			string id=TwMediaHelper.GetVideoIdYoutube(expanded_url);
			
			if(string.IsNullOrWhiteSpace(id))
				return false;
			
			XDocument yt_info=null;
			try
			{
				yt_info=TwMediaHelper.GetMediaInfoYoutube(id);
			}
			catch(Exception web_ex)
			{
				#if DEBUG
				Helper.OnDebugMessage(string.Format("Error: '{0}' while get info for '{1}'",web_ex.Message,expanded_url));
				#endif
			}
			
			if(yt_info==null)
				return false;
			
			//разбираем полученный xml
			//в нём ещё есть и разное прочее полезное
			XNamespace media_ns = yt_info.Root.GetNamespaceOfPrefix("media");
			
			Description=(string)((from el in yt_info.Descendants(media_ns+"title") select el).First());
			OriginalUrl=expanded_url;

			var elem_enum =
				(from el in yt_info.Descendants(media_ns + "thumbnail") select el);
			foreach(var elem in elem_enum)
			{
				thumbnail_list.Add
					(new TwMediaThumbnail()
					 {
					 	Height = int.Parse(elem.Attribute("height").Value),
					 	Width = int.Parse(elem.Attribute("width").Value),
					 	ThumbnailUrl = elem.Attribute("url").Value
					 });
			}
			return true;
		}
	}
	#endregion


	public class TwMediaInfo
	{
		public string OriginalUrl { get; private set; }
		public string Description { get; private set; }
		public string Title { get; private set; }
		public SortedSet<TwMediaThumbnail> Thumbnails { get; private set; }
		public int OriginalWidth { get; private set; }
		public int OriginalHeight { get; private set; }

		private TwMediaInfo()
		{
			Thumbnails = new SortedSet<TwMediaThumbnail>();
		}

		public TwMediaThumbnail SelectThumbnail(int max_width=360,int max_height=240)
		{
			foreach (var media_thumb in Thumbnails)
			{
				//перечисление должно начать с самых боьших
				if((max_width>media_thumb.Width)&&(max_height>media_thumb.Height))
					return media_thumb;
			}
			//или вернуть самый малый thumb
			if(Thumbnails.Count>0)
				return Thumbnails.Last();
			else
				return null;
		}

		static TwMediaInfo()
		{
			m_FactoryList = new List<ITwMediaInfoFactory>();

			var interface_type = typeof(ITwMediaInfoFactory);
			var implements_types = AppDomain.CurrentDomain.GetAssemblies().ToList()
				.SelectMany(assembly => assembly.GetTypes())
				.Where(type => interface_type.IsAssignableFrom(type) && type.IsClass);
			foreach (var one_factory_type in implements_types)
			{
				ITwMediaInfoFactory instance = Activator.CreateInstance(one_factory_type) as ITwMediaInfoFactory;
				m_FactoryList.Add(instance);
			}
		}
		private static List<ITwMediaInfoFactory> m_FactoryList;
		
		private static ITwMediaInfoFactory SelectFactory(TwEntityUrl ent_url)
		{
			foreach(var factory in m_FactoryList)
			{
				if(factory.Supported(ent_url))
					return factory;
			}
			
			return null;
		}
		
		public static bool Supported(TwEntityUrl entity_url)
		{
			return SelectFactory(entity_url)!=null;
		}
		
		public static TwMediaInfo FromUri(TwEntityUrl ent_url)
		{
			if (ent_url == null)
				return null;

			#if DEBUG
			Helper.OnDebugMessage(string.Format("Create TwMediaInfo from {0}",ent_url.ExpandedUrl));
			#endif
			
			ITwMediaInfoFactory factory=TwMediaInfo.SelectFactory(ent_url);
			
			if(factory==null)
				return null;
			
			TwMediaInfo ret = null;

			string f_OriginalUrl=null;
			string f_Title=null;
			string f_Description=null;
			int f_OriginalWidth=0;
			int f_OriginalHeight=0;
			IEnumerable<TwMediaThumbnail> f_Thumbnails=null;
			
			if(factory.FetchProperies
			   (ent_url,
			    out f_OriginalUrl,
			    out f_Title,
			    out f_Description,
			    out f_OriginalWidth,
			    out f_OriginalHeight,
			    out f_Thumbnails))
			{
				ret=new TwMediaInfo()
				{
					Description=f_Description,
					OriginalHeight=f_OriginalHeight,
					OriginalUrl=f_OriginalUrl,
					OriginalWidth=f_OriginalWidth,
					Title=f_Title
				};
				foreach(var thumb in f_Thumbnails)
				{
					ret.Thumbnails.Add(thumb);
				}
				return ret;
			}
			else
			{
				return null;
			}
		}

		public static TwMediaInfo FromTwMediaEntity(TwEntityMedia ent_media)
		{
			if (ent_media == null)
				return null;

			TwMediaInfo ret = new TwMediaInfo()
			{
				Description = "Twitter",
				OriginalUrl = ent_media.MediaUrlHttps
			};

			if (ent_media.Sizes != null)
			{
				if (ent_media.Sizes.Large != null)
				{
					TwMediaThumbnail large = new TwMediaThumbnail()
					{
						Height = ent_media.Sizes.Large.Height,
						ThumbnailUrl = ent_media.MediaUrlHttps + ":large",
						Width = ent_media.Sizes.Large.Width
					};
					ret.Thumbnails.Add(large);
				}

				if (ent_media.Sizes.Medium != null)
				{
					TwMediaThumbnail medium = new TwMediaThumbnail()
					{
						Height = ent_media.Sizes.Medium.Height,
						ThumbnailUrl = ent_media.MediaUrlHttps + ":medium",
						Width = ent_media.Sizes.Medium.Width
					};
					ret.Thumbnails.Add(medium);
				}

				if (ent_media.Sizes.Small != null)
				{
					TwMediaThumbnail small = new TwMediaThumbnail()
					{
						Height = ent_media.Sizes.Small.Height,
						ThumbnailUrl = ent_media.MediaUrlHttps + ":small",
						Width = ent_media.Sizes.Small.Width
					};
					ret.Thumbnails.Add(small);
				}

				if (ent_media.Sizes.Thumb != null)
				{
					TwMediaThumbnail thumb = new TwMediaThumbnail()
					{
						Height = ent_media.Sizes.Thumb.Height,
						ThumbnailUrl = ent_media.MediaUrlHttps + ":thumb",
						Width = ent_media.Sizes.Thumb.Width
					};
					ret.Thumbnails.Add(thumb);
				}
			}

			return ret;
		}
	}

	public class TwMediaThumbnail:IComparable<TwMediaThumbnail>,IComparable
	{
		public string ThumbnailUrl { get; set; }
		public int Width { get; set; }
		public int Height { get; set; }

		#region Члены IComparable<TwMediaThumbnail>
		int IComparable<TwMediaThumbnail>.CompareTo(TwMediaThumbnail other)
		{
			if (other == null)
				return 1;

			int res = this.Width - other.Width;

			if (res != 0)
				return -res;

			res= this.Height - other.Height;
			return -res;
		}
		#endregion

		#region Члены IComparable
		int IComparable.CompareTo(object obj)
		{
			if (obj == null)
				return 1;

			TwMediaThumbnail other = obj as TwMediaThumbnail;
			return ((IComparable<TwMediaThumbnail>)this).CompareTo(other);
		}
		#endregion

		public override bool Equals(object obj)
		{
			if (obj == null)
				return false;

			return ((IComparable)this).CompareTo(obj) == 0;
		}

		public override int GetHashCode()
		{
			int hashCode = 0;
			unchecked
			{
				hashCode += Width.GetHashCode();
				hashCode += 1000000009 * Height.GetHashCode();
			}
			return hashCode;
		}
	}
}
